#ifndef _SPEECHANALYSISCUT_H_
#define _SPEECHANALYSISCUT_H_

#include <cstdio>
#include <cstring>
#include <string>
#include <sstream>
#include <vector>
#include <map>
#include <utility>


using namespace std;

#define  _CRT_SECURE_NO_WARNINGS 1

class SpeechProcessState{
public:
	SpeechProcessState();
	~SpeechProcessState();
	enum ERROR_TYPE{
		CORRECT_STATE = 0,
		ERROR_READ_FILE,
		ERROR_WRITEN_FILE,
		ERROR_OTHERS
	};
};

// only for basic C++ TYPE, and cannot be used with Object ! [8/17/2015 Administrator]
template <typename T>
class Type2Char
{
public:
	Type2Char() {
		
	}

	~Type2Char()
	{}
	const char* operator()(const T* data_in_)
	{
		std::stringstream ss;
		ss << *data_in_;
		if (ss.good())
		{
			return ss.str().data();
		}
		else
		{
			std::cerr << "error occurred !\r\n";
		}
	}

private:
};

class BoundaryInfo
{
public:
	BoundaryInfo():acc_obj_(0.0f), is_need_adding_(false)
	{
		;
	}
	BoundaryInfo operator=(const BoundaryInfo& rhs){
		if (this == &rhs){
			return *this;
		}
		
		this->acc_obj_ = rhs.get_obj();
		this->is_need_adding_ = rhs.need_adding();
		this->revised_boundary_ = rhs.get_revised_boundary();
		this->curr_obj_ = rhs.get_curr_obj();
		this->prev_boundary_ = rhs.get_prev_boundary();
		this->node_i_ = rhs.get_node_index();
		this->father_i_ = rhs.get_father_node_index();
		return *this;
	}
	BoundaryInfo* Copy() const {
		BoundaryInfo* new_boundary = new BoundaryInfo;
		new_boundary->acc_obj_ = acc_obj_;
		new_boundary->curr_obj_ = curr_obj_;
		new_boundary->prev_boundary_ = prev_boundary_;
		new_boundary->revised_boundary_ = revised_boundary_;
		new_boundary->is_need_adding_ = is_need_adding_;
		new_boundary->node_i_ = node_i_;
		new_boundary->father_i_ = father_i_;
		return new_boundary;
	}
	virtual ~BoundaryInfo()
	{
		;
	}
	inline void set_obj(float adding_obj){
		acc_obj_ = adding_obj;
	}
	inline void adding_obj(float obj_value, bool is_need_init=false){
		if (is_need_init)
		{
			set_obj(obj_value);
			return;
		}
		acc_obj_ += obj_value;
	}
	inline float get_obj() const {
		return acc_obj_;
	}
	inline bool need_adding() const{
		return is_need_adding_;
	}
	inline void set_adding_state(bool adding_state){
		is_need_adding_ = adding_state;
	}
	inline void set_curr_obj(float new_obj_){
		curr_obj_ = new_obj_;
	}
	inline void set_revised_boundary(float revised_boundary){
		revised_boundary_ = revised_boundary;
	}
	inline void change_boundary(float boundary_change_, bool is_adding)
	{
		if (is_adding)
		{
			revised_boundary_ += boundary_change_;
		} else{
			revised_boundary_ -= boundary_change_;
		}

	}
	inline void set_original_boundary(float old_boundary){
		prev_boundary_ = old_boundary;
	}
	inline float get_revised_boundary()const{
		return revised_boundary_;
	}
	inline float get_curr_obj()const {
		return curr_obj_;
	}
	inline float get_prev_boundary() const{
		return prev_boundary_;
	}
	inline void set_node_index(int index){
		node_i_ = index;
	}
	inline int get_node_index() const {
		return node_i_;
	}
	inline void set_father_node_index(int father_index){
		father_i_ = father_index;
	}
	inline int get_father_node_index() const {
		return father_i_;
	}

private:
	float acc_obj_;
	float curr_obj_;
	float prev_boundary_;
	float revised_boundary_;
	int node_i_;
	int father_i_;
	bool is_need_adding_;
};



typedef struct 
{
	char riff_id[4];         // 4 chars for storing 'RIFF'.4 chars and one '\0' can be useful for using strcmp
	unsigned  int file_size;	// 4chars, size of file
	char wave_id[4];			//4chars, "WAVE"
	char fmt_id[4];			//4chars, "fmt ", the last char is blank
	long int format_length; //4 chars
	unsigned short format_tag; //2chars
	unsigned short channels;   //2chars
	unsigned  int sample_rate;  //4chars
	unsigned  int avg_bytes_sec;  //4 chars
	unsigned short block_align; //2 chars
	unsigned short bits_per_sample;    // 2 chars, 16 bits data
	char data_id[4]; //4chars
	unsigned  int data_size; //4chars,  32 bits data
}WAV_HEADER;


typedef struct AdHocAnnotation
{
	float annotation_start = 0.0;
	float annotation_end = 0.0;
	std::vector<int> adhoc_value;
};

typedef struct HTKHeader
{
	int feature_total_num;
	int sample_period;
	unsigned short singleVectorCharsNum;
	unsigned short sampleKind;
	HTKHeader(){
		feature_total_num = 0;
		sample_period = 0;
		singleVectorCharsNum = 0;
		sampleKind = 0;
	}
	~HTKHeader(){
		;
	}
	char* SequentialHtkHeader(){
		stringstream ss;
		ss << feature_total_num << sample_period << singleVectorCharsNum << sampleKind;
		int headercharnum = sizeof(int) * 2 + sizeof(unsigned short) * 2;
		char* seq = new char[headercharnum + 1];
		strcpy(seq, ss.str().data());
		seq[headercharnum + 1] = '\0';
		return seq;
	}
	HTKHeader ReverseSequentialHtkHeader(){
		HTKHeader unseq;
		return unseq;
	}
};


class Options
{
public:
	Options();
	virtual ~Options();
	int registration_options(const string& option_key_);
	int add_options(const string& option_key_, const string& option_value_);
	bool find_options(const string& option_key_) const;
	string get_option_value(const string& option_key_);

protected:
private:
	std::map<string, string> options_;

};


class WAVE
{
public:
	WAVE(const char* filename, const char* wave_id);
	~WAVE();

	inline int WaveLength(){return m_waveHeader->data_size;}
	inline int SampleRate(){return m_waveHeader->sample_rate;}
	inline int BitsPerSample(){return m_waveHeader->bits_per_sample;}
	inline  char* GetWaveId()  {return &m_waveId[0];}
	inline  char* GetWaveDir() {return &m_waveDir[0];}
	int MakeWavHeader();
	//int PrintWaveInfo();
	inline void ChangeWaveLength(unsigned new_length){m_waveHeader->data_size = new_length;}
	inline void ChangeWaveSampleRate(unsigned new_samplerate){m_waveHeader->sample_rate = new_samplerate;}
	void GetFileName(const char* complete_dir, char* single_filename);
	int DoSpeechSplit(unsigned subseg_id, float start, float end, float offset, 
		unsigned sample_rate, const char* output_dir);
	int WriteWaveHeader(const WAV_HEADER* wave_header, FILE* fp);
	
protected:
	WAV_HEADER* getWaveHeader();
	WAV_HEADER* ReadWaveHeader(const char* file_name);
	short* ReadWaveDate(const char* file_name);
	
	short* GetWaveData(){return m_waveData;}

private:
	short* m_waveData;
	WAV_HEADER* m_waveHeader;
	char m_waveId[128];
	char m_waveDir[512];

};
class SpeechAnalysis
{
public:
	SpeechAnalysis();
	~SpeechAnalysis();

	void SpeechCutTool();
protected:
	int ReadWaveHeader();

private:
	WAVE* m_wave;


};

int PrintVectorInfo(const std::vector<int>& in_vector_);

bool IsFileAWave(const char* file_name);
bool MakeDirectory(const std::string& dir_name);
int GetFilePath(std::string &strFilePath);

SpeechProcessState::ERROR_TYPE AddHeader4Utterance(const string& utterance_file_);

int GenerateIntRandom(std::vector<int>& results_, const int results_size_);
int computeIntersection(const std::map<string,string>& aContainer, const std::map<string, string>& bContainer, std::vector<string>& resultContainer);

int SplitKaldiScp( Options& paragram_options_);

WAV_HEADER* MakeWaveHeader(const unsigned long file_size_, unsigned short channel_nums_,
	unsigned long sample_rate_, unsigned long avg_bytes_sec, unsigned short bits_per_sample_);


std::vector<char> ParseString2Chars(const string& str_in_);
std::vector<std::string> SplitString(const std::string& str_in_, const string& str_sep_, int split_num=-1);

SpeechProcessState::ERROR_TYPE enumGlobalVar(const string& );
int ProcessTimitTextTrans(Options& paragram_options_);
bool ProcAnnoatation(const string& a_annotation_file_);

int WriteVectorWithHtkMode(const string& out_file_name_, const std::vector<std::vector<string> >& to_write_);
int ComputeFrameNum(float seg_start_, float seg_end_, float sample_rate_, float frame_shift_, float frame_dur_);
bool AdjustAnnotation(const string& annotation_file_name_, float sample_rate_, float frame_shift_, float frame_dur_);
std::pair<int, int> ComputeMinOffSet(int total_num, int unit_num, int max_renmain);
inline int ComputeSpeechLengthByFrame(int frame_shift, int shift_num, int frame_length){
	return frame_shift*shift_num + frame_length - frame_shift;
}

int FindBestBoundary(const std::vector<float>& seg_boundaries);
int InitSearchTreeNet(std::vector<std::vector<BoundaryInfo> >* searchTree, const std::vector<float>& seg_boundaries);
int TreeLayerAveragingPrune(std::vector<BoundaryInfo>& searchTreeLayer);
int TreeLayerNodeNumPrune(std::vector<BoundaryInfo>& searchTreeLayer, int prune_width);

std::pair<float, float> ComputeForwardBackObjs(int total_num, int unit_num, int max_renmain);

#endif
